---
layout: './_Home.astro'
---
import Pre from '@/components/Pre.astro';
import NumberFlow from '@number-flow/react'
import TimingsDemo from './_demos/Timings'
import IsolateDemo from './_demos/Isolate'
import ContinuousDemo from './_demos/Continuous'
import SuffixDemo from './_demos/Suffix'
import TrendDemo from './_demos/Trend'
import StylingDemo from './_demos/Styling'
import Digits from './_Digits.astro'
import Match from '@/components/Match.astro'
import Meta from '@/components/Meta.astro'
import AnimationsOnTheWeb from '@/components/AnimationsOnTheWeb.astro'
import Type from '@/components/Type.astro'
import Heading from '@/components/Heading.astro'
import Union from '@/components/Union.astro'
import Link from '@/components/Link.astro'
import Note from '@/components/Note.astro'
import Group from './examples/_Group/index.astro'
export { getStaticPaths } from '@/lib/framework'
export const components = {a: Link, pre: Pre}

{/* We need an empty match for a complicated reason related to async and collecting the TOC headers in order */}
<Match>
<Heading class="sr-only" value="Basic usage" />
</Match>

<div className='xl:pre-first-line:hidden first:*:mt-0'>
<Match react>
```jsx
// Basic usage
import NumberFlow from '@number-flow/react'

<NumberFlow value={123} />
```
</Match>
<Match vue>
```vue
<!-- Basic usage -->
<script setup>
import NumberFlow from '@number-flow/vue'
</script>
<template>
	<NumberFlow :value="123" />
</template>
```
</Match>
<Match svelte>
```svelte
<!-- Basic usage -->
<script>
import NumberFlow from '@number-flow/svelte'
</script>

<NumberFlow value={123} />
```
</Match>
</div>

<Match>
`<NumberFlow>` will automatically transition when the `value` prop changes.
</Match>

<AnimationsOnTheWeb />

<Match>
<Heading value="Props" />
</Match>

<h3 id="format">
	<code>format<Type>: Intl.NumberFormatOptions</Type></code>
</h3>

Formatting options for the number; see MDN for [a full reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#options).

<Match react>
```jsx
<NumberFlow format={{ notation: 'compact' }} value={value} />
```
</Match>
<Match vue>
```vue
<NumberFlow :format="{ notation: 'compact' }" :value />
```
</Match>
<Match svelte>
```svelte
<NumberFlow format={{ notation: 'compact' }} {value} />
```
</Match>

<h3 id="locales">
<code>locales<Type>: Intl.LocalesArgument</Type></code>
</h3>

The locale(s) for the number; see MDN for [more information](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#locales).

<h3>
	<code>prefix<Type>: string</Type>, suffix<Type>: string</Type></code>
</h3>

A custom prefix or suffix for the number.

<SuffixDemo client:visible defaultValue='code'>
<Match react>
```jsx
<NumberFlow
	value={value}
	format={{ style: 'currency', currency: 'USD', trailingZeroDisplay: 'stripIfInteger' }}
	suffix="/mo"
/>
```
</Match>
<Match vue>
```vue
<NumberFlow
	:value
	:format="{ style: 'currency', currency: 'USD', trailingZeroDisplay: 'stripIfInteger' }"
	suffix="/mo"
/>
```
</Match>
<Match svelte>
```svelte
<NumberFlow
	{value}
	format={{ style: 'currency', currency: 'USD', trailingZeroDisplay: 'stripIfInteger' }}
	suffix="/mo"
/>
```
</Match>
</SuffixDemo>

### Timings

There are three props to customize the animation timings. Each accept an [`EffectTiming`](https://developer.mozilla.org/en-US/docs/Web/API/AnimationEffect/getTiming#return_value) object:

<TimingsDemo client:visible defaultValue='code'>
<Match react>
```jsx
<NumberFlow
	// Used for layout-related transforms:
	transformTiming={{ duration: 750, easing: 'linear(...)' }}
	// Used for the digit spin animations.
	// Will fall back to `transformTiming` if unset:
	spinTiming={{ duration: 750, easing: 'linear(...)' }}
	// Used for fading in/out characters:
	opacityTiming={{ duration: 350, easing: 'ease-out' }}
/>
```
</Match>
<Match vue>
```vue
<NumberFlow
	:transformTiming="{
		// Used for layout-related transforms:
		duration: 750, easing: 'linear(...)'
	}"
	:spinTiming="{
		// Used for the digit spin animations.
		// Will fall back to `transformTiming` if unset:
		duration: 750, easing: 'linear(...)'
	}"
	:opacityTiming="{
		// Used for fading in/out characters:
		duration: 350, easing: 'ease-out'
	}"
/>
```
</Match>
<Match svelte>
```svelte
<NumberFlow
	transformTiming={{
		// Used for layout-related transforms:
		duration: 750, easing: 'linear(...)'
	}}
	spinTiming={{
		// Used for the digit spin animations.
		// Will fall back to `transformTiming` if unset:
		duration: 750, easing: 'linear(...)'
	}}
	opacityTiming={{
		// Used for fading in/out characters:
		duration: 350, easing: 'ease-out'
	}}
/>
```
</Match>
</TimingsDemo>

For spring-based easings, I'd recommend [Kevin Grajeda's generator](https://www.kvin.me/css-springs)
or [easing.dev](https://www.easing.dev/).

<h3>
	<code>trend<Type>: <Union types={["number", "(oldValue: number, value: number) => number"]} /></Type></code>
	<Meta>Default: `(oldValue, value) => Math.sign(value - oldValue)`</Meta>
</h3>

Controls the direction of the digits. If `trend` is or returns

- `+1:` the digits always go up.
- `0:` each digit goes up if it increases and down if it decreases. This can be useful if you
want to animate number changes without conveying an overall trend ([example](https://x.com/pontusab/status/1825941664189526067)).
- `-1:` The digits always go down.

<TrendDemo client:visible />

<Match react>
<h3>
	<code>isolate<Type>: boolean</Type></code>
	<Meta>Default: `false`</Meta>
</h3>

If `isolate` is set, `<NumberFlow>`'s transitions are isolated from any other layout changes
that may occur in the same update. Has no effect when inside a [`<NumberFlowGroup>`](#grouping).

<IsolateDemo client:visible />
</Match>

<h3>
	<code>animated<Type>: boolean</Type></code>
	<Meta>Default: `true`</Meta>
</h3>

Can be set to `false` to disable all animations and finish any current ones.
See the [input example](/examples/#input) for a usage scenario.

<h3>
	<code>digits<Type>: Record{'<number, { max?: number }>'}</Type></code>
</h3>

Configure digits based on their position in the number (i.e. for 342.5, the positions are: <Digits />). This can be helpful for time-related displays,
to ensure e.g. 59 -> 00. See the [countdown example](/examples/#countdown) for a demo.

<Note>
`digits` is not reactive to save on bundle size.
If you need it to be reactive, please submit a [feature request](https://github.com/barvian/number-flow/discussions/new?category=ideas).
</Note>

<h3>
	<code>willChange<Type>: boolean</Type></code>
	<Meta>Default: `false`</Meta>
</h3>

If set, NumberFlow applies [`will-change` properties](https://developer.mozilla.org/en-US/docs/Web/CSS/will-change) to relevant elements.
This can be useful if:
* Your number is guaranteed to change frequently
* You experience unwanted repositioning when a transition completes

Note that "excessive use of `will-change` will result in excessive memory use" (source: [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/will-change)).

<h3 id="respect-motion-preference">
	<code>respectMotionPreference<Type>: boolean</Type></code>
	<Meta>Default: `true`</Meta>
</h3>

Can be set to `false` to animate regardless of the user's reduced motion preference.

<h3>
	<code>plugins<Type>: Plugin[]</Type></code>
</h3>

Plugins to apply to the component. Currently there's only one plugin, `continuous`, which
makes the number transitions appear to pass through in-between numbers:

<ContinuousDemo client:visible>
<Match react>
```jsx
import NumberFlow, { continuous } from '@number-flow/react'

<NumberFlow
	plugins={[continuous]}
	value={value}
/>
```
</Match>
<Match vue>
```vue
<script setup>
import NumberFlow, { continuous } from '@number-flow/vue'
</script>
<template>
	<NumberFlow
		:plugins="[continuous]"
		:value="123"
	/>
</template>
```
</Match>
<Match svelte>
```svelte
<script>
import NumberFlow, { continuous } from '@number-flow/svelte'
</script>

<NumberFlow
	plugins={[continuous]}
	value={123}
/>
```
</Match>
</ContinuousDemo>

This plugin has no effect if `trend` is `0`.

<Match vue>
---
<Heading value="Emits"/>
</Match>
<Match svelte>
---
<Heading value="Events"/>
</Match>

<h3><code><Match react="onAnimationsStart" vue="animationsstart" svelte="animationsstart" /><Type>: (e: CustomEvent) => void</Type></code></h3>

Triggered when update animations start.
<Match vue react>
Not to be confused with the built-in
<Match react>`onAnimationStart`</Match><Match vue>[`animationstart` event](https://developer.mozilla.org/en-US/docs/Web/API/Element/animationstart_event)</Match>, which
would trigger for animations on the `<NumberFlow>` element itself.
</Match>

<h3><code><Match react="onAnimationsFinish" vue="animationsfinish" svelte="animationsfinish" /><Type>: (e: CustomEvent) => void</Type></code></h3>

Triggered when update animations finish.

---

<Match>
<Heading value="Styling" />
</Match>

NumberFlow uses a [custom element](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements) under the hood, and exposes [parts](https://developer.mozilla.org/en-US/docs/Web/CSS/::part)
for styling purposes:

<StylingDemo client:visible>
<Match react>
```css
number-flow-react::part(suffix) {
	margin-left: .0625em;
	font-weight: normal;
	font-size: 0.75em;
	color: var(--muted);
}
```
</Match>
<Match vue>
```css
number-flow-vue::part(suffix) {
	margin-left: .0625em;
	font-weight: normal;
	font-size: 0.75em;
	color: var(--muted);
}
```
</Match>
<Match svelte>
```css
number-flow-svelte::part(suffix) {
	margin-left: .0625em;
	font-weight: normal;
	font-size: 0.75em;
	color: var(--muted);
}
```
</Match>
</StylingDemo>

You can use your browser's inspector to see which [`part` attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/part)
are available to style. Note that changing the `font-size` of digits is difficult
due to the CSS techniques NumberFlow uses.

<Note class="mb-[2.7em]">
`::part` styles may cause a flash of unstyled content in [old browsers](https://caniuse.com/declarative-shadow-dom).
<details>
<summary>See workaround</summary>
You can use feature detection
to apply `::part` styles only to browsers that support [Declarative Shadow DOM](https://web.dev/articles/declarative-shadow-dom) (DSD). Add the following snippet to your `<head>`:

```html
<script>
	if (
		HTMLTemplateElement.prototype.hasOwnProperty('shadowRootMode') ||
		HTMLTemplateElement.prototype.hasOwnProperty('shadowRoot') // old Chrome/Edge
	)
		document.documentElement.setAttribute('data-supports-dsd', '')
</script>
```

Then ensure your `::part` styles use it:

<Match react>
```css
:root[data-supports-dsd] number-flow-react::part(suffix) {
	font-size: 0.75rem;
}
```
</Match>
<Match vue>
```css
:root[data-supports-dsd] number-flow-vue::part(suffix) {
	font-size: 0.75rem;
}
```
</Match>
<Match svelte>
```css
:root[data-supports-dsd] number-flow-svelte::part(suffix) {
	font-size: 0.75rem;
}
```
</Match>
If you're using Tailwind, you can do this with a custom variant:
```js
// tailwind.config.js
import plugin from 'tailwindcss/plugin'

export default {
	// ...
	plugins: [
		plugin(({ matchVariant }) => {
			matchVariant('part', (p) => `:root[data-supports-dsd] &::part(${p})`)
		})
	]
}
```

<Match react>
```tsx
<NumberFlow className="part-[suffix]:text-xs" />
```
</Match>
<Match vue>
```vue
<NumberFlow class="part-[suffix]:text-xs" />
```
</Match>
<Match svelte>
```svelte
<NumberFlow class="part-[suffix]:text-xs" />
```
</Match>
</details>
</Note>

There's also some CSS properties you can use to style the component:

<h3>
	<code>{'--'}number-flow-mask-[height|width]<Type>: {'<length>'}</Type></code>
	<Meta>Default: `.25em` | `.5em`</Meta>
</h3>

These adjust the height and width of the gradient fade-out masks at the edges of the number.
`--number-flow-mask-height` also gets used as the top and bottom padding for the number.

<h3>
	<code>{'--'}number-flow-char-height<Type>: {'<length>'}</Type></code>
	<Meta>Default: `1em`</Meta>
</h3>

Sets the height of each character. This can be used to adjust the spacing between
digits during spin animations.

### <code>font-variant-numeric<span className="text-muted">:</span> tabular-nums</code>

Ensures all numbers are the same width, which can prevent digits from shifting during transitions.
See [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/font-variant-numeric#numeric-spacing-values) for more information.

{/* <TabularNumsDemo client:visible /> */}

---

<Heading value="Grouping"/>

If a `<NumberFlow>` affects another `<NumberFlow>`'s position, you can wrap
them in a `<NumberFlowGroup>` to properly sync their transitions:

<Group />

`<NumberFlowGroup>` doesn't render an element or accept any props.

---

<Match>
<Heading slot="react" value="Hooks" />
<Heading slot="vue" value="Composables" />
<Heading slot="svelte" value="Stores" />
</Match>

<Match as="h3">
<code slot="react">useCanAnimate(opts<Type>?: {'{'} respectMotionPreference?: boolean {'}'}</Type>)<Type>: boolean</Type></code>
<code slot="vue">useCanAnimate(opts<Type>?: {'{'} respectMotionPreference?: {'MaybeRefOrGetter<boolean> }'}</Type>)<Type>: {'ComputedRef<boolean>'}</Type></code>
<code slot="svelte">getCanAnimate(opts<Type>?: {'{'} respectMotionPreference?: boolean {'}'}</Type>)<Type>: {'Readable<boolean>'}</Type></code>
</Match>

Returns <Match react>`true`</Match><Match vue>a computed ref whose value is `true`</Match><Match svelte>a readable store whose value is `true`</Match> if NumberFlow can animate, i.e. the browser supports the [required features](https://caniuse.com/mdn-css_types_mod)
and (optionally) the user is [okay with motion](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion).
<Match react>See the [Motion for React example](/examples#motion-for-react) for a usage scenario.</Match>

---

<Match>
<Heading value="Limitations" />
</Match>

* [Scientific](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#scientific) and [engineering](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#engineering) notations aren't supported.
* [Non-Latin digits](https://github.com/barvian/number-flow/issues/8) and [RTL locales](https://github.com/barvian/number-flow/issues/93) aren't currently supported.
* Backgrounds and borders on `<NumberFlow>` won't scale smoothly during transitions.
<Match react>I'd recommend using [Motion for React](https://motion.dev/docs/react-quick-start) for these, as it's more composable than any built-in solution could be. See the Motion for React layout animations [example](/examples#motion-for-react).</Match>
